package org.black.phoneUtility.sms.schedule.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.black.phoneUtility.database.sqlite.PhoneUtilitySQLiteOpenHelper;
import org.black.phoneUtility.sms.schedule.app.service.SmsScheduleSendingService;
import org.black.phoneUtility.sms.schedule.database.dataModel.SmsSchedule;
import org.black.phoneUtility.sms.schedule.database.dataModel.SmsScheduleRecipient;
import org.black.phoneUtility.sms.schedule.provider.SmsScheduleContract;
import org.black.phoneUtility.sms.schedule.service.exception.EndDateTimeHasExpiredException;
import org.black.phoneUtility.sms.schedule.service.exception.NextScheduleTimeIsEarlierThanEndDateTimeException;
import org.black.phoneUtility.sms.schedule.service.exception.NoMessageException;
import org.black.phoneUtility.sms.schedule.service.exception.NoRecipientsException;
import org.black.phoneUtility.sms.schedule.service.exception.ScheduleTimeHasExpiredException;
import org.black.phoneUtility.sms.schedule.service.exception.SmsScheduleDoesNotExistException;

import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

public class SmsScheduleService {

	private static final String LOG_TAG = SmsScheduleService.class.getName();

	private Context context;
	private PhoneUtilitySQLiteOpenHelper phoneUtilitySQLiteOpenHelper;

	private static final long SECONDS_IN_MILLISECONDS = 1000l;
	private static final long MINUTE_IN_SECONDS = 60l;
	private static final long HOUR_IN_SECONDS = MINUTE_IN_SECONDS * 60l;
	private static final long DAY_IN_SECONDS = HOUR_IN_SECONDS * 24l;
	private static final long WEEK_IN_MILLISECONDS = DAY_IN_SECONDS * 7l
			* SECONDS_IN_MILLISECONDS;

	public SmsScheduleService(Context context) {
		this.context = context;
		this.phoneUtilitySQLiteOpenHelper = new PhoneUtilitySQLiteOpenHelper(
				this.context);
	}

	public Uri insertOrUpdate(SmsSchedule smsSchedule) throws RemoteException,
			OperationApplicationException {
		if (smsSchedule == null) {
			throw new IllegalArgumentException("smsSchedule is null!");
		}
		ArrayList<ContentProviderOperation> contentProviderOperations = new ArrayList<ContentProviderOperation>();

		boolean isInsert = true;
		ContentValues smsScheduleContentValues = new ContentValues();
		if (smsSchedule.getId() != null) {
			isInsert = false;
		}
		smsScheduleContentValues.put(SmsScheduleContract.SmsSchedules.SUBJECT,
				TextUtils.isEmpty(smsSchedule.getSubject()) == true ? ""
						: smsSchedule.getSubject());
		smsScheduleContentValues.put(SmsScheduleContract.SmsSchedules.MESSAGE,
				TextUtils.isEmpty(smsSchedule.getMessage()) == true ? ""
						: smsSchedule.getMessage());
		Calendar scheduleDateTimeCalendar = Calendar.getInstance();
		scheduleDateTimeCalendar.setTimeInMillis(smsSchedule
				.getScheduleDateTime().getTime());
		scheduleDateTimeCalendar.set(Calendar.MILLISECOND, 0);
		scheduleDateTimeCalendar.set(Calendar.SECOND, 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.SCHEDULE_DATE_TIME,
				scheduleDateTimeCalendar.getTimeInMillis());
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME,
				scheduleDateTimeCalendar.getTimeInMillis());
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_TYPE,
				smsSchedule.getRepeatType());
		if (smsSchedule.getRepeatEvery() != null) {
			smsScheduleContentValues.put(
					SmsScheduleContract.SmsSchedules.REPEAT_EVERY,
					smsSchedule.getRepeatEvery());
		}
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_SUNDAY,
				true == smsSchedule.isRepeatOnSunday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_MONDAY,
				true == smsSchedule.isRepeatOnMonday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_TUESDAY,
				true == smsSchedule.isRepeatOnTuesday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_WEDNESDAY,
				true == smsSchedule.isRepeatOnWednesday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_THURSDAY,
				true == smsSchedule.isRepeatOnThursday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_FRIDAY,
				true == smsSchedule.isRepeatOnFriday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.REPEAT_ON_SATURDAY,
				true == smsSchedule.isRepeatOnSaturday() ? 1 : 0);
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.IS_ACTIVATED,
				true == smsSchedule.isActivated() ? 1 : 0);
		smsScheduleContentValues.put(SmsScheduleContract.SmsSchedules.END_TYPE,
				smsSchedule.getEndType());
		if (smsSchedule.getEndAfterTimes() != null) {
			smsScheduleContentValues.put(
					SmsScheduleContract.SmsSchedules.END_AFTER_TIMES,
					smsSchedule.getEndAfterTimes());
		}
		if (smsSchedule.getEndOnSpecificDateTime() != null) {
			Calendar calendar = Calendar.getInstance();
			calendar.setTimeInMillis(smsSchedule.getEndOnSpecificDateTime()
					.getTime());
			calendar.set(Calendar.MILLISECOND, 0);
			calendar.set(Calendar.SECOND, 0);
			smsScheduleContentValues.put(
					SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME,
					calendar.getTimeInMillis());
		}
		if (isInsert) {
			smsScheduleContentValues.put(
					SmsScheduleContract.SmsSchedules.CREATION_DATE_TIME,
					System.currentTimeMillis());
		}
		smsScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.MODIFICATION_DATE_TIME,
				System.currentTimeMillis());

		int insertSmsScheduleIndex = 0;
		if (isInsert == true) {
			insertSmsScheduleIndex = contentProviderOperations.size();
			contentProviderOperations.add(ContentProviderOperation
					.newInsert(SmsScheduleContract.SmsSchedules.CONTENT_URI)
					.withValues(smsScheduleContentValues).build());
		} else {
			contentProviderOperations.add(ContentProviderOperation
					.newUpdate(SmsScheduleContract.SmsSchedules.CONTENT_URI)
					.withSelection(
							SmsScheduleContract.SmsSchedules._ID + " = ? ",
							new String[] { smsSchedule.getId().toString() })
					.withValues(smsScheduleContentValues).build());
		}
		if (isInsert == false) {
			if (smsSchedule.getSmsScheduleRecipients() == null
					|| smsSchedule.getSmsScheduleRecipients().size() == 0) {
				contentProviderOperations
						.add(ContentProviderOperation
								.newDelete(
										SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI)
								.withSelection(
										SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
												+ " = ? ",
										new String[] { smsSchedule.getId()
												.toString() }).build());
			} else {
				List<String> smsScheduleRecipientIds = new ArrayList<String>();
				for (SmsScheduleRecipient smsScheduleSmsRecipient : smsSchedule
						.getSmsScheduleRecipients()) {
					if (smsScheduleSmsRecipient.getId() != null
							&& smsScheduleSmsRecipient.getId() != 0) {
						smsScheduleRecipientIds.add(smsScheduleSmsRecipient
								.getId().toString());
					}
				}
				if (smsScheduleRecipientIds.size() > 0) {
					List<String> whereArgus = new ArrayList<String>();
					whereArgus.add(smsSchedule.getId().toString());
					whereArgus.addAll(smsScheduleRecipientIds);
					contentProviderOperations
							.add(ContentProviderOperation
									.newDelete(
											SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI)
									.withSelection(
											SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
													+ " = ? "
													+ " AND "
													+ SmsScheduleContract.SmsScheduleRecipients._ID
													+ " NOT IN ("
													+ generatePlaceHolder(smsScheduleRecipientIds
															.size()) + ")",
											whereArgus
													.toArray(new String[whereArgus
															.size()])).build());
				} else {
					contentProviderOperations
							.add(ContentProviderOperation
									.newDelete(
											SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI)
									.withSelection(
											SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
													+ " = ? ",
											new String[] { smsSchedule.getId()
													.toString() }).build());
				}
			}
		}
		for (SmsScheduleRecipient smsScheduleSmsRecipient : smsSchedule
				.getSmsScheduleRecipients()) {
			ContentProviderOperation.Builder builder = null;
			if (smsScheduleSmsRecipient.getId() == null) {
				builder = ContentProviderOperation
						.newInsert(
								SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI)
						.withValue(
								SmsScheduleContract.SmsScheduleRecipients.CREATION_DATE_TIME,
								System.currentTimeMillis());
				if (isInsert == true) {
					builder.withValueBackReference(
							SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID,
							insertSmsScheduleIndex);
				} else {
					builder.withValue(
							SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID,
							smsSchedule.getId());
				}
			} else {
				builder = ContentProviderOperation.newUpdate(
						SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI)
						.withSelection(
								SmsScheduleContract.SmsScheduleRecipients._ID
										+ " = ? ",
								new String[] { smsScheduleSmsRecipient.getId()
										.toString() });
			}
			builder.withValue(SmsScheduleContract.SmsScheduleRecipients.TYPE,
					smsScheduleSmsRecipient.getType())
					.withValue(
							SmsScheduleContract.SmsScheduleRecipients.PHONE_NUMBER,
							smsScheduleSmsRecipient.getPhoneNumber())
					.withValue(
							SmsScheduleContract.SmsScheduleRecipients.MODIFICATION_DATE_TIME,
							System.currentTimeMillis());
			if (smsScheduleSmsRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT
					|| smsScheduleSmsRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT_GROUP) {
				builder.withValue(
						SmsScheduleContract.SmsScheduleRecipients.NAME,
						smsScheduleSmsRecipient.getName());
				if (smsScheduleSmsRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT_GROUP) {
					builder.withValue(
							SmsScheduleContract.SmsScheduleRecipients.GROUP_NAME,
							smsScheduleSmsRecipient.getGroupName());
				}
			}
			contentProviderOperations.add(builder.build());
		}
		if (isInsert == false) {
			contentProviderOperations
					.add(ContentProviderOperation
							.newUpdate(
									SmsScheduleContract.SmsScheduleSendingRecords.CONTENT_URI)
							.withSelection(
									SmsScheduleContract.SmsScheduleSendingRecords.REFERENCE_SMS_SCHEDULE_ID
											+ " = ? ",
									new String[] { smsSchedule.getId()
											.toString() })
							.withValue(
									SmsScheduleContract.SmsScheduleSendingRecords.IS_VALID,
									0).build());
		}

		ContentProviderResult[] results = this.context.getContentResolver()
				.applyBatch(SmsScheduleContract.AUTHORITY,
						contentProviderOperations);

		Uri result = results[insertSmsScheduleIndex].uri;
		if (result == null) {
			result = ContentUris.withAppendedId(
					SmsScheduleContract.SmsSchedules.CONTENT_URI,
					smsSchedule.getId());
		}

		long nextSchedule = this.nextScheduleTimeInMilliseconds(result);
		ContentValues updateScheduleContentValues = new ContentValues();
		updateScheduleContentValues.put(
				SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME,
				nextSchedule);
		this.context.getContentResolver().update(result,
				updateScheduleContentValues, null, null);

		this.context.getContentResolver().notifyChange(
				SmsScheduleContract.SmsSchedules.CONTENT_URI, null);
		if (isInsert == false && smsSchedule.isActivated() == false) {
			Intent smsScheduleServiceIntent = new Intent(this.context,
					SmsScheduleSendingService.class);
			smsScheduleServiceIntent.putExtra(
					SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_ID,
					smsSchedule.getId());
			smsScheduleServiceIntent
					.putExtra(
							SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED,
							false);

			this.context.startService(smsScheduleServiceIntent);
		}
		return result;
	}

	public Uri copy(long smsScheduleId) throws RemoteException,
			OperationApplicationException {
		SmsSchedule smsSchedule = this.read(smsScheduleId);
		if (smsSchedule != null) {
			smsSchedule.setId(null);
			smsSchedule.setSubject(smsSchedule.getSubject() + "("
					+ this.context.getString(android.R.string.copy) + ")");
			Date creationDateTime = new Date();
			smsSchedule.setActivated(false);
			smsSchedule.setCreationDateTime(creationDateTime);
			smsSchedule.setModificationDateTime(creationDateTime);
			if (smsSchedule.getSmsScheduleRecipients() != null) {
				for (SmsScheduleRecipient smsScheduleRecipient : smsSchedule
						.getSmsScheduleRecipients()) {
					smsScheduleRecipient.setId(null);
					smsScheduleRecipient.setCreationDateTime(creationDateTime);
				}
			}

			return this.insertOrUpdate(smsSchedule);
		}
		return null;
	}

	public SmsSchedule read(Long id) {
		SmsSchedule result = new SmsSchedule();
		List<SmsScheduleRecipient> smsScheduleRecipients = new ArrayList<SmsScheduleRecipient>();
		result.setSmsScheduleRecipients(smsScheduleRecipients);
		if (id == null || id == 0) {
			result.setActivated(false);
			result.setRepeatType(SmsScheduleContract.SmsSchedules.REPEAT_TYPE_NONE);
			result.setRepeatEvery(10);
			result.setSubject("");
			result.setEndType(SmsScheduleContract.SmsSchedules.END_TYPE_NEVER);
			result.setEndAfterTimes(10);
			Calendar calendar = Calendar.getInstance();
			calendar.add(Calendar.HOUR_OF_DAY, 1);
			calendar.set(Calendar.MILLISECOND, 0);
			calendar.set(Calendar.SECOND, 0);
			result.setScheduleDateTime(calendar.getTime());
		} else {
			Cursor smsScheduleCursor = null;
			try {
				smsScheduleCursor = this.context.getContentResolver().query(
						ContentUris.withAppendedId(
								SmsScheduleContract.SmsSchedules.CONTENT_URI,
								id), null, null, null, null);
				if (smsScheduleCursor.moveToFirst() == true) {
					result.setId(id);
					result.setSubject(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.SUBJECT)) == true ? null
							: smsScheduleCursor.getString(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.SUBJECT)));
					result.setMessage(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MESSAGE)) == true ? null
							: smsScheduleCursor.getString(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MESSAGE)));
					result.setScheduleDateTime(new Date(
							smsScheduleCursor.getLong(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.SCHEDULE_DATE_TIME))));
					result.setNextScheduleDateTime(new Date(
							smsScheduleCursor.getLong(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME))));
					result.setRepeatType(smsScheduleCursor.getInt(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_TYPE)));
					result.setRepeatEvery(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY)) == true ? null
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY)));
					result.setRepeatOnSunday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SUNDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SUNDAY)) == 1 ? true
									: false);
					result.setRepeatOnMonday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_MONDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_MONDAY)) == 1 ? true
									: false);
					result.setRepeatOnTuesday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_TUESDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_TUESDAY)) == 1 ? true
									: false);
					result.setRepeatOnWednesday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_WEDNESDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_WEDNESDAY)) == 1 ? true
									: false);
					result.setRepeatOnThursday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_THURSDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_THURSDAY)) == 1 ? true
									: false);
					result.setRepeatOnFriday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_FRIDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_FRIDAY)) == 1 ? true
									: false);
					result.setRepeatOnSaturday(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SATURDAY)) == true ? false
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SATURDAY)) == 1 ? true
									: false);
					result.setActivated(smsScheduleCursor.getInt(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.IS_ACTIVATED)) == 1 ? true
							: false);
					result.setEndType(smsScheduleCursor.getInt(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_TYPE)));
					result.setEndAfterTimes(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_AFTER_TIMES)) == true ? null
							: smsScheduleCursor.getInt(smsScheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_AFTER_TIMES)));
					result.setEndOnSpecificDateTime(smsScheduleCursor.isNull(smsScheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME)) == true ? null
							: new Date(
									smsScheduleCursor.getLong(smsScheduleCursor
											.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME))));
					Cursor recipientsCursor = null;
					try {
						recipientsCursor = this.context
								.getContentResolver()
								.query(SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI,
										null,
										SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
												+ " = ? ",
										new String[] { id.toString() },
										SmsScheduleContract.SmsScheduleRecipients.CREATION_DATE_TIME
												+ " ASC, "
												+ SmsScheduleContract.SmsScheduleRecipients.MODIFICATION_DATE_TIME
												+ " ASC ", null);
						if (recipientsCursor.moveToFirst() == true) {
							do {
								SmsScheduleRecipient smsScheduleRecipient = new SmsScheduleRecipient();
								smsScheduleRecipient
										.setId(recipientsCursor.getLong(recipientsCursor
												.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients._ID)));
								smsScheduleRecipient
										.setType(recipientsCursor.getInt(recipientsCursor
												.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients.TYPE)));
								smsScheduleRecipient
										.setPhoneNumber(recipientsCursor.getString(recipientsCursor
												.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients.PHONE_NUMBER)));
								if (smsScheduleRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT
										|| smsScheduleRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT_GROUP) {
									smsScheduleRecipient
											.setName(recipientsCursor.getString(recipientsCursor
													.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients.NAME)));
									if (smsScheduleRecipient.getType() == SmsScheduleContract.SmsScheduleRecipients.TYPE_CHOOSE_FROM_CONTACT_GROUP) {
										smsScheduleRecipient
												.setGroupName(recipientsCursor
														.getString(recipientsCursor
																.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients.GROUP_NAME)));
									}
								}
								smsScheduleRecipients.add(smsScheduleRecipient);
							} while (recipientsCursor.moveToNext() == true);
						}
					} finally {
						if (recipientsCursor != null) {
							try {
								recipientsCursor.close();
							} catch (Throwable throwable) {

							}
						}

					}
				} else {
					Log.e(LOG_TAG, "smsSchedule does not exist! id: " + id);
					return null;
				}
			} finally {
				if (smsScheduleCursor != null) {
					try {
						smsScheduleCursor.close();
					} catch (Throwable throwable) {

					}
				}
			}
		}
		return result;
	}

	public boolean switchActivatedStatus(long smsScheduleId)
			throws NoMessageException, NoRecipientsException,
			ScheduleTimeHasExpiredException, SmsScheduleDoesNotExistException,
			EndDateTimeHasExpiredException,
			NextScheduleTimeIsEarlierThanEndDateTimeException {
		SQLiteDatabase sqLiteDatabase = null;
		Cursor cursor = null;
		boolean result = false;
		Long nextScheduleTimeInMilliseconds = null;
		long currentTimeMillis = System.currentTimeMillis();
		try {
			sqLiteDatabase = this.phoneUtilitySQLiteOpenHelper
					.getWritableDatabase();
			sqLiteDatabase.beginTransaction();
			cursor = sqLiteDatabase.query(
					PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SCHEDULES,
					null, SmsScheduleContract.SmsSchedules._ID + " = ? ",
					new String[] { String.valueOf(smsScheduleId) }, null, null,
					null);
			if (cursor.moveToFirst() == true) {
				if (cursor
						.isNull(cursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MESSAGE)) == true
						|| TextUtils
								.isEmpty(cursor.getString(cursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MESSAGE))) == true) {
					throw new NoMessageException("Message is empty");
				}
				if (cursor
						.getInt(cursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_TYPE)) == SmsScheduleContract.SmsSchedules.END_TYPE_ON_SPECIFIC_DATETIME) {
					long endDateTime = cursor
							.getLong(cursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME));
					if (endDateTime <= currentTimeMillis) {
						throw new EndDateTimeHasExpiredException(
								"endDateTime: " + endDateTime
										+ " >= currentTimeMillis: "
										+ currentTimeMillis);
					}
				}

				Cursor recipientsCount = null;
				try {
					recipientsCount = sqLiteDatabase
							.rawQuery(
									"SELECT count("
											+ SmsScheduleContract.SmsScheduleRecipients._ID
											+ ") FROM "
											+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.RECIPIENTS
											+ " WHERE "
											+ SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
											+ " = ? ", new String[] { String
											.valueOf(smsScheduleId) });
					recipientsCount.moveToFirst();
					if (recipientsCount.getInt(0) == 0) {
						throw new NoRecipientsException("No Recipients");
					}
				} finally {
					if (recipientsCount != null) {
						try {
							recipientsCount.close();
						} catch (Throwable throwable) {
							Log.e(LOG_TAG, "Fail to close recipientsCount",
									throwable);
						}
					}
				}

				boolean isActivated = (1 == cursor
						.getInt(cursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.IS_ACTIVATED))) ? true
						: false;
				ContentValues smsScheduleContentValues = new ContentValues();
				if (isActivated == false) {
					smsScheduleContentValues.put(
							SmsScheduleContract.SmsSchedules.IS_ACTIVATED, 1);
					nextScheduleTimeInMilliseconds = this
							.nextScheduleTimeInMilliseconds(cursor);
					if (nextScheduleTimeInMilliseconds <= System
							.currentTimeMillis()) {
						throw new ScheduleTimeHasExpiredException(new Date(
								nextScheduleTimeInMilliseconds).toString());
					}
					if (cursor
							.getInt(cursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_TYPE)) == SmsScheduleContract.SmsSchedules.END_TYPE_ON_SPECIFIC_DATETIME) {
						long endDateTime = cursor
								.getLong(cursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME));
						if (nextScheduleTimeInMilliseconds >= endDateTime) {
							NextScheduleTimeIsEarlierThanEndDateTimeException exception = new NextScheduleTimeIsEarlierThanEndDateTimeException(
									"Next Schedule Date Time: "
											+ nextScheduleTimeInMilliseconds
											+ ", end Date Time: " + endDateTime);
							exception
									.setNextScheduleDateTime(nextScheduleTimeInMilliseconds);
							exception.setEndDateTime(currentTimeMillis);
							throw exception;
						}
					}
					smsScheduleContentValues
							.put(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME,
									nextScheduleTimeInMilliseconds);
				} else {
					smsScheduleContentValues.put(
							SmsScheduleContract.SmsSchedules.IS_ACTIVATED, 0);
				}
				smsScheduleContentValues
						.put(SmsScheduleContract.SmsSchedules.MODIFICATION_DATE_TIME,
								currentTimeMillis);
				int updateResult = sqLiteDatabase
						.update(PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SCHEDULES,
								smsScheduleContentValues,
								SmsScheduleContract.SmsSchedules._ID + " = ? ",
								new String[] { String.valueOf(smsScheduleId) });
				if (updateResult == 0) {
					throw new SmsScheduleDoesNotExistException(smsScheduleId
							+ " does not exist.");
				}
				ContentValues updateSendingRecordContentValues = new ContentValues();
				updateSendingRecordContentValues.put(
						SmsScheduleContract.SmsScheduleSendingRecords.IS_VALID,
						0);
				sqLiteDatabase
						.update(PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SENDING_RECORDS,
								updateSendingRecordContentValues,
								SmsScheduleContract.SmsScheduleSendingRecords.REFERENCE_SMS_SCHEDULE_ID
										+ " = ? ",
								new String[] { String.valueOf(smsScheduleId) });

				result = !isActivated;
			} else {
				throw new SmsScheduleDoesNotExistException(smsScheduleId
						+ " does not exist.");
			}

			this.context.getContentResolver().notifyChange(
					SmsScheduleContract.SmsSchedules.CONTENT_URI, null);

			Intent smsScheduleServiceIntent = new Intent(this.context,
					SmsScheduleSendingService.class);
			smsScheduleServiceIntent.putExtra(
					SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_ID,
					smsScheduleId);
			smsScheduleServiceIntent
					.putExtra(
							SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED,
							result);
			smsScheduleServiceIntent
					.putExtra(
							SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME,
							currentTimeMillis);
			this.context.startService(smsScheduleServiceIntent);
			sqLiteDatabase.setTransactionSuccessful();

			Intent scheduleSendingService = new Intent(context,
					SmsScheduleSendingService.class);
			Bundle argumentsForSchedule = new Bundle();
			argumentsForSchedule
					.putLong(
							SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME,
							currentTimeMillis);
			argumentsForSchedule
					.putBoolean(
							SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED,
							result);
			argumentsForSchedule.putLong(
					SmsScheduleSendingService.ARGUMENTS_SMS_SCHEDULE_ID,
					smsScheduleId);
			scheduleSendingService.putExtras(argumentsForSchedule);
			context.startService(scheduleSendingService);
			Log.d(LOG_TAG, "Start SmsScheduleSendingService!");

			return result;
		} finally {
			if (cursor != null) {
				try {
					cursor.close();
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "Fail to close cursor", throwable);
				}
			}
			if (sqLiteDatabase != null) {
				sqLiteDatabase.endTransaction();
				try {
					sqLiteDatabase.close();
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "Fail to close sqLiteDatabase", throwable);
				}
			}
		}
	}

	public Long nextScheduleTimeInMilliseconds(Uri uri) {
		Cursor cursor = this.context.getContentResolver().query(uri, null,
				null, null, null);
		cursor.moveToFirst();
		Long result = this.nextScheduleTimeInMilliseconds(cursor);
		cursor.close();
		return result;
	}

	public Long nextScheduleTimeInMilliseconds(Cursor cursor) {
		Long result = null;

		int repeatType = cursor
				.getInt(cursor
						.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_TYPE));
		long currentTimeInMillis = System.currentTimeMillis();
		long scheduleDateTime = cursor
				.getLong(cursor
						.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.SCHEDULE_DATE_TIME));
		Calendar scheduleCalendar = Calendar.getInstance();
		scheduleCalendar.setTimeInMillis(scheduleDateTime);
		scheduleCalendar.set(Calendar.MILLISECOND, 0);
		scheduleCalendar.set(Calendar.SECOND, 0);

		if (repeatType == SmsScheduleContract.SmsSchedules.REPEAT_TYPE_NONE
				&& scheduleCalendar.getTimeInMillis() >= System
						.currentTimeMillis()) {
			Log.d(LOG_TAG, "schedule date time is after than current time!");
			return scheduleCalendar.getTimeInMillis();
		}

		switch (repeatType) {
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_NONE:
			result = scheduleDateTime;
			break;
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_EVERY_MINUTE:
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_HOURLY:
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_DAILY: {
			Calendar resultCalendar = Calendar.getInstance();
			if (scheduleCalendar.getTimeInMillis() >= System
					.currentTimeMillis()) {
				return scheduleCalendar.getTimeInMillis();
			} else {

				Long repeatEvery = cursor
						.getLong(cursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY));
				if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_EVERY_MINUTE == repeatType) {
					repeatEvery *= SmsScheduleService.MINUTE_IN_SECONDS;
				} else if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_HOURLY == repeatType) {
					repeatEvery *= SmsScheduleService.HOUR_IN_SECONDS;
				} else if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_DAILY == repeatType) {
					repeatEvery *= SmsScheduleService.DAY_IN_SECONDS;
				}

				long tempResult = this.nextOccurenceTimeInMillis(
						scheduleDateTime, currentTimeInMillis, repeatEvery,
						SECONDS_IN_MILLISECONDS);
				resultCalendar.setTimeInMillis(tempResult);
				resultCalendar.set(Calendar.MILLISECOND, 0);
				resultCalendar.set(Calendar.SECOND, 0);
				if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_EVERY_MINUTE == repeatType) {
					if (resultCalendar.getTimeInMillis() <= System
							.currentTimeMillis()) {
						resultCalendar.add(Calendar.MINUTE,
								repeatEvery.intValue());
					}
				} else if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_HOURLY == repeatType) {
					resultCalendar.set(Calendar.MINUTE,
							scheduleCalendar.get(Calendar.MINUTE));
					if (resultCalendar.getTimeInMillis() <= System
							.currentTimeMillis()) {
						resultCalendar.add(Calendar.HOUR_OF_DAY,
								repeatEvery.intValue());
					}
				} else if (SmsScheduleContract.SmsSchedules.REPEAT_TYPE_DAILY == repeatType) {
					resultCalendar.set(Calendar.MINUTE,
							scheduleCalendar.get(Calendar.MINUTE));

					resultCalendar.set(Calendar.HOUR_OF_DAY,
							scheduleCalendar.get(Calendar.HOUR_OF_DAY));
					Log.e(LOG_TAG, resultCalendar.getTime().toString());
					if (resultCalendar.getTimeInMillis() <= System
							.currentTimeMillis()) {
						resultCalendar.add(Calendar.DAY_OF_YEAR,
								repeatEvery.intValue());
					}
				}
				result = resultCalendar.getTimeInMillis();
				break;
			}
		}
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_WEEKLY: {
			Long repeatEvery = cursor
					.getLong(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY));
			Long weekRemainder = ((currentTimeInMillis / WEEK_IN_MILLISECONDS) - (scheduleDateTime / WEEK_IN_MILLISECONDS))
					% repeatEvery;
			int weekOffset = 0;
			if (weekRemainder > 0l) {
				weekOffset = repeatEvery.intValue() - weekRemainder.intValue();
			}
			Calendar resultCalendar = Calendar.getInstance();
			resultCalendar.add(Calendar.WEEK_OF_YEAR, weekOffset);
			resultCalendar.set(Calendar.MILLISECOND, 0);
			resultCalendar.set(Calendar.SECOND, 0);
			Calendar currentCalendar = Calendar.getInstance();
			int currentDayOfWeek = currentCalendar.get(Calendar.DAY_OF_WEEK);
			boolean repeatOnSunday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SUNDAY))) ? true
					: false;
			boolean repeatOnMonday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_MONDAY))) ? true
					: false;
			boolean repeatOnTuesday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_TUESDAY))) ? true
					: false;
			boolean repeatOnWednesday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_WEDNESDAY))) ? true
					: false;
			boolean repeatOnThursday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_THURSDAY))) ? true
					: false;
			boolean repeatOnFriday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_FRIDAY))) ? true
					: false;
			boolean repeatOnSaturday = (1 == cursor
					.getInt(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_ON_SATURDAY))) ? true
					: false;
			if (repeatOnSunday == false && repeatOnMonday == false
					&& repeatOnTuesday == false && repeatOnWednesday == false
					&& repeatOnThursday == false && repeatOnFriday == false
					&& repeatOnSaturday == false) {
				switch (scheduleCalendar.get(Calendar.DAY_OF_WEEK)) {
				case Calendar.SUNDAY:
					repeatOnSunday = true;
					break;
				case Calendar.MONDAY:
					repeatOnMonday = true;
					break;
				case Calendar.TUESDAY:
					repeatOnTuesday = true;
					break;
				case Calendar.WEDNESDAY:
					repeatOnWednesday = true;
					break;
				case Calendar.THURSDAY:
					repeatOnThursday = true;
					break;
				case Calendar.FRIDAY:
					repeatOnFriday = true;
					break;
				case Calendar.SATURDAY:
					repeatOnSaturday = true;
					break;
				}
			}
			boolean repeatDays[] = new boolean[] { repeatOnSunday,
					repeatOnMonday, repeatOnTuesday, repeatOnWednesday,
					repeatOnThursday, repeatOnFriday, repeatOnSaturday };
			int repeatDaysLength = repeatDays.length;
			int beginDay = 0;
			for (int i = 0; i < repeatDaysLength; i++) {
				if (repeatDays[i] == true) {
					beginDay = i + 1;
					break;
				}
			}
			int endDay = 0;
			for (int i = 6; i >= 0; i--) {
				if (repeatDays[i] == true) {
					endDay = i + 1;
					break;
				}
			}
			if (scheduleCalendar.getTimeInMillis() >= System
					.currentTimeMillis()) {
				int scheduleCalendarDayOfWeek = scheduleCalendar
						.get(Calendar.DAY_OF_WEEK);
				if (repeatDays[scheduleCalendarDayOfWeek - 1] == true) {
					return scheduleCalendar.getTimeInMillis();
				} else {
					if (scheduleCalendarDayOfWeek < endDay) {
						scheduleCalendar.add(Calendar.DAY_OF_YEAR, endDay
								- scheduleCalendarDayOfWeek);
					} else {
						scheduleCalendar.add(Calendar.DAY_OF_YEAR, beginDay
								- scheduleCalendarDayOfWeek);
						scheduleCalendar.add(Calendar.DAY_OF_YEAR, Long
								.valueOf(repeatEvery).intValue() * 7);
					}
					return scheduleCalendar.getTimeInMillis();
				}
			}
			if (beginDay == 0 || endDay == 0) {
				if (cursor
						.isNull(cursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME)) == true) {
					result = cursor
							.getLong(cursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.SCHEDULE_DATE_TIME));
				} else {
					result = cursor
							.getLong(cursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME));
				}
			} else {
				int nextOccurDay = 0;
				int nextOccurDayOffset = 0;

				int length = currentDayOfWeek + repeatDays.length;
				for (int i = currentDayOfWeek - 1; i < length; i++) {
					int index = i % 7;
					if (repeatDays[index] == true) {
						nextOccurDay = index + 1;
						nextOccurDayOffset = nextOccurDay - currentDayOfWeek;
						break;
					}
				}

				resultCalendar
						.add(Calendar.DAY_OF_YEAR,
								nextOccurDayOffset < 0 ? (repeatEvery
										.intValue() * 7 + nextOccurDayOffset)
										: nextOccurDayOffset);

				resultCalendar.set(Calendar.MINUTE,
						scheduleCalendar.get(Calendar.MINUTE));

				resultCalendar.set(Calendar.HOUR_OF_DAY,
						scheduleCalendar.get(Calendar.HOUR_OF_DAY));

				if (resultCalendar.getTimeInMillis() <= System
						.currentTimeMillis() && nextOccurDayOffset >= 0) {
					if (resultCalendar.get(Calendar.DAY_OF_WEEK) >= endDay) {
						resultCalendar.add(
								Calendar.DAY_OF_YEAR,
								repeatEvery.intValue()
										* 7
										+ beginDay
										- resultCalendar
												.get(Calendar.DAY_OF_WEEK));
					} else {
						nextOccurDay = 1;
						for (int i = resultCalendar.get(Calendar.DAY_OF_WEEK); i < repeatDaysLength; i++) {
							if (repeatDays[i] == true) {
								nextOccurDay = i + 1;
								break;
							}
						}
						resultCalendar.add(Calendar.DAY_OF_YEAR, nextOccurDay
								- resultCalendar.get(Calendar.DAY_OF_WEEK));
					}
				}
			}
			result = resultCalendar.getTimeInMillis();
			break;
		}
		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_MONTHLY: {
			if (scheduleCalendar.getTimeInMillis() >= System
					.currentTimeMillis()) {
				return scheduleCalendar.getTimeInMillis();
			}
			Long repeatEvery = cursor
					.getLong(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY));
			int scheduleYear = scheduleCalendar.get(Calendar.YEAR);
			int scheduleMonth = scheduleCalendar.get(Calendar.MONTH);
			int scheduleDayOfMonth = scheduleCalendar
					.get(Calendar.DAY_OF_MONTH);
			int scheduleHourOfDay = scheduleCalendar.get(Calendar.HOUR_OF_DAY);
			int scheduleMinute = scheduleCalendar.get(Calendar.MINUTE);

			Calendar resultCalendar = Calendar.getInstance();
			int currentYear = resultCalendar.get(Calendar.YEAR);
			int currentMonth = resultCalendar.get(Calendar.MONTH);

			int monthRemainder = ((currentYear * 12 + currentMonth) - (scheduleYear * 12 + scheduleMonth))
					% repeatEvery.intValue();
			int monthOffset = 0;
			if (monthRemainder > 0) {
				monthOffset = repeatEvery.intValue() - monthRemainder;
			}
			resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
			resultCalendar.set(Calendar.MILLISECOND, 0);
			resultCalendar.set(Calendar.SECOND, 0);
			resultCalendar.set(Calendar.HOUR_OF_DAY, scheduleHourOfDay);
			resultCalendar.set(Calendar.MINUTE, scheduleMinute);
			resultCalendar.add(Calendar.MONTH, monthOffset);
			if (scheduleDayOfMonth > resultCalendar
					.getActualMaximum(Calendar.DAY_OF_MONTH)) {
				resultCalendar.set(Calendar.DAY_OF_MONTH,
						resultCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
			} else {
				resultCalendar.set(Calendar.DAY_OF_MONTH, scheduleDayOfMonth);
			}

			if (resultCalendar.getTimeInMillis() <= System.currentTimeMillis()) {
				resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
				resultCalendar.add(Calendar.MONTH, repeatEvery.intValue());
				if (scheduleDayOfMonth > resultCalendar
						.getActualMaximum(Calendar.DAY_OF_MONTH)) {
					resultCalendar.set(Calendar.DAY_OF_MONTH, resultCalendar
							.getActualMaximum(Calendar.DAY_OF_MONTH));
				} else {
					resultCalendar.set(Calendar.DAY_OF_MONTH,
							scheduleDayOfMonth);
				}
			}

			result = resultCalendar.getTimeInMillis();
			break;
		}

		case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_YEARLY:
			if (scheduleCalendar.getTimeInMillis() >= System
					.currentTimeMillis()) {
				return scheduleCalendar.getTimeInMillis();
			}
			Long repeatEvery = cursor
					.getLong(cursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_EVERY));
			int scheduleYear = scheduleCalendar.get(Calendar.YEAR);
			int scheduleMonth = scheduleCalendar.get(Calendar.MONTH);
			int scheduleDayOfMonth = scheduleCalendar
					.get(Calendar.DAY_OF_MONTH);
			int scheduleHourOfDay = scheduleCalendar.get(Calendar.HOUR_OF_DAY);
			int scheduleMinute = scheduleCalendar.get(Calendar.MINUTE);

			Calendar resultCalendar = Calendar.getInstance();
			int currentYear = resultCalendar.get(Calendar.YEAR);

			int yearRemainder = (currentYear - scheduleYear)
					% repeatEvery.intValue();
			int yearOffset = 0;
			if (yearRemainder > 0) {
				yearOffset = repeatEvery.intValue() - yearRemainder;
			}
			resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
			resultCalendar.set(Calendar.MILLISECOND, 0);
			resultCalendar.set(Calendar.SECOND, 0);
			resultCalendar.set(Calendar.HOUR_OF_DAY, scheduleHourOfDay);
			resultCalendar.set(Calendar.MINUTE, scheduleMinute);
			resultCalendar.set(Calendar.MONTH, scheduleMonth);
			resultCalendar.add(Calendar.YEAR, yearOffset);
			if (scheduleDayOfMonth > resultCalendar
					.getActualMaximum(Calendar.DAY_OF_MONTH)) {
				resultCalendar.set(Calendar.DAY_OF_MONTH,
						resultCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
			} else {
				resultCalendar.set(Calendar.DAY_OF_MONTH, scheduleDayOfMonth);
			}

			if (resultCalendar.getTimeInMillis() <= System.currentTimeMillis()) {
				resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
				resultCalendar.add(Calendar.YEAR, repeatEvery.intValue());
				if (scheduleDayOfMonth > resultCalendar
						.getActualMaximum(Calendar.DAY_OF_MONTH)) {
					resultCalendar.set(Calendar.DAY_OF_MONTH, resultCalendar
							.getActualMaximum(Calendar.DAY_OF_MONTH));
				} else {
					resultCalendar.set(Calendar.DAY_OF_MONTH,
							scheduleDayOfMonth);
				}
			}
			result = resultCalendar.getTimeInMillis();
			break;
		default:
			break;
		}

		return result;
	}

	public int getSendingCount(long smsScheduleId) {
		SQLiteDatabase sqLiteDatabase = null;
		Cursor sendingCountQuery = null;
		try {
			sqLiteDatabase = this.phoneUtilitySQLiteOpenHelper
					.getReadableDatabase();
			sendingCountQuery = sqLiteDatabase
					.rawQuery(
							"SELECT count("
									+ SmsScheduleContract.SmsScheduleSendingRecords._ID
									+ ") FROM "
									+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Schedule.SENDING_RECORDS
									+ " WHERE "
									+ SmsScheduleContract.SmsScheduleSendingRecords.REFERENCE_SMS_SCHEDULE_ID
									+ " = ? "
									+ " AND "
									+ SmsScheduleContract.SmsScheduleSendingRecords.IS_VALID
									+ " = ?",
							new String[] { String.valueOf(smsScheduleId),
									String.valueOf(1) });
			sendingCountQuery.moveToFirst();
			int count = sendingCountQuery.getInt(0);
			return count;
		} finally {
			if (sendingCountQuery != null) {
				try {
					sendingCountQuery.close();
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "Fail to close recipientsCount", throwable);
				}
			}
			if (sqLiteDatabase != null) {
				try {
					sqLiteDatabase.close();
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "Fail to close sqLiteDatabase", throwable);
				}
			}
		}
	}

	private long nextOccurenceTimeInMillis(long start, long current,
			long repeatEvery, long repeatUnit) {
		long offset = repeatEvery
				- (((current / repeatUnit) - (start / repeatUnit)) % repeatEvery);
		return current + offset * repeatUnit;
	}

	private String generatePlaceHolder(int length) {
		if (length < 1) {
			throw new RuntimeException("Do not need any placeholders.");
		} else {
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.append("?");
			for (int i = 1; i < length; i++) {
				stringBuilder.append(",?");
			}
			return stringBuilder.toString();
		}
	}
}
